/*

  CRISP Programmable Editor
  =========================

  File:    sniff.cr 
  Author:  Oberhofer Alexander
  Company: TakeFive Software GmbH
  Date:    Oct. 17 1996

*/

# include       <crisp.h>
# include       <proto.h> 
# include       "sniff.h" 

static string   sniff_server_program = "sniffemacs";
static string   connection_name = "SNiFF";
/*  IPC channel identifier  */
static int      ipc_id = -1;
/*  PID of the server program */
static int      server_pid;
/*  Input from server program */
static string   input_line;
static int      sc_debug = TRUE;
extern string	automounter_prefix;

/*  Initialization */
void main()
{
}

/* Macro to start with */
void sniff()
{
	extern list menu_bar_hook;
    menu_bar_hook += "sniff_create_menu_entry";
}

/* Definition of Menu Entries */
void sniff_create_menu_entry()
{    
	dynamic obj_list;
	int     pos;
	list    mlist;
	
	mlist = make_list(
					  DBOX_MENU_BUTTON, "&SNiFF+",
					  DBOX_SUB_GROUP_START,
					  DBOX_MENU_ITEM, "&Classes",
					  DBOX_SUB_GROUP_START,
					  DBOX_MENU_ITEM, "&Toggle Between declaration and definition",
					  DBOX_CALLBACK, "sniff_icon \"e\"",
					  DBOX_MENU_ITEM, "&Show base class",
					  DBOX_CALLBACK, "sniff_icon \"s\"",
					  DBOX_MENU_ITEM, "&Overriden Methods",
					  DBOX_CALLBACK, "sniff_icon \"m\"",
					  DBOX_MENU_ITEM, "&Browse Class",
					  DBOX_CALLBACK, "sniff_icon \"w\"",
					  DBOX_MENU_ITEM, "Show Class in &Heirarchy",
					  DBOX_CALLBACK, "sniff_icon \"t\"",
					  DBOX_MENU_ITEM, "Show Class in &Restricted Hierarchy",
					  DBOX_CALLBACK, "sniff_icon \"T\"",
					  DBOX_GROUP_END,
					  DBOX_MENU_ITEM, "&Retriever",
					  DBOX_SUB_GROUP_START,
					  DBOX_MENU_ITEM, "&Run Retriever on Selection",
					  DBOX_CALLBACK, "sniff_icon \"r\"",
					  DBOX_MENU_ITEM, "&Next Retriever match",
					  DBOX_CALLBACK, "sniff_icon \"R\"",
					  DBOX_GROUP_END,
					  DBOX_MENU_ITEM, "&Symbols",
					  DBOX_SUB_GROUP_START,
					  DBOX_MENU_ITEM, "&Goto Symbol",
					  DBOX_CALLBACK, "sniff_icon \"g\"",
					  DBOX_MENU_ITEM, "&Find Symbol",
					  DBOX_CALLBACK, "sniff_icon \"f\"",
					  DBOX_GROUP_END,
					  DBOX_MENU_ITEM, "&X-Reference",
					  DBOX_SUB_GROUP_START,
					  DBOX_MENU_ITEM, "&Refers To",
					  DBOX_CALLBACK, "sniff_icon \"x\"",
					  DBOX_MENU_ITEM, "Referred &By",
					  DBOX_CALLBACK, "sniff_icon \"X\"",
					  DBOX_MENU_ITEM, "Referred By &Component",
					  DBOX_CALLBACK, "sniff_icon \"C\"",
					  DBOX_MENU_ITEM, "Refers &To Component",
					  DBOX_CALLBACK, "sniff_icon \"c\"",
					  DBOX_GROUP_END,

					  DBOX_MENU_ITEM, "Start Connection",
					  DBOX_CALLBACK, "sniff_start",
					  DBOX_MENU_ITEM, "Stop Connection",
					  DBOX_CALLBACK, "sniff_stop",
					  DBOX_GROUP_END);

	pos = re_search(SF_UNIX, "&Help", obj_list);
	obj_list = delete_nth(obj_list, pos - 1, 999) + mlist +
		delete_nth(obj_list, 0, pos - 1);
}


/* Start sniffemacs */
void start_server_process()
{
	if (ipc_id >= 0) 
		{
			kill_server_process();
		}
	
	sc_log_string("Info", "Starting sniffemacs\n");
	
	set_busy_state(TRUE);
	
	ipc_id = ipc_create(IPC_ANON_PIPE, sniff_server_program + " " + connection_name);
	if (ipc_id < 0) 
		{
			set_busy_state(FALSE);
			create_notice("Failed to start the BMS message server\n" +
						  sniff_server_program + "\n", " Ok ");
			return;
		}

	/***********************************************/
	/*   Retrieve  process ID of the program just  */
	/*   started.                                  */
	/***********************************************/
	ipc_status(ipc_id, NULL, server_pid);
	
	ipc_register(ipc_id, IPC_TRIGGER_PROCESS_DEATH, "::sc_process_death " + ipc_id);
	ipc_register(ipc_id, IPC_TRIGGER_READ, "::sc_read_callback " + ipc_id);

	register_macro(REG_WRITE_FILE, "::sc_reg_write_file " + ipc_id);
	
	set_busy_state(FALSE);
}


static void kill_server_process()
{

	if (ipc_id >= 0) 
		{
			broadcast_message("q");
			ipc_close(ipc_id);
		}

	if (server_pid > 0)
		kill(server_pid, 9);
	
	ipc_id = -1;
	
	input_line = "";
	sc_log_string("Info", "Stopping sniffemacs\n");
	unregister_macro(REG_WRITE_FILE,        "sc_reg_write_file");
}


static void broadcast_message(string command)
{
       sc_write_string(command);
}


static int counter = 0;
void sniff_icon(string name)
{    
	string  dir;
	string  s;
	string  line;
	int i;
	string file;
	string command;
	
	if (inq_marked()) 
		{
			s = trim(get_region());
			if ((i = index(s, "\n")) > 0)
				s = substr(s, 1, i - 1);
		}
	if (s == "" && substr(name,1,1) != "R" && substr(name,1,1) != "u" && substr(name,1,1) != "z") 
		{
			create_notice("You need to mark a function name, class name,\n\
parameter or variable before you can use this option.\n\n", " Ok ");
			return;
		}
	
	inq_names(dir, NULL, file);

	dir = sub("^" + quote_regexp(automounter_prefix), "", dir);

	sprintf(line, "%d", inq_byte_pos());

	switch (substr(name,1,1)) 
		{
		case "u" :
		case "z" :
		case "R" :
			command = name +  dir + "\n";
			break;
		case "m" :
		case "e" :
			command = name + dir + " " + line + "\n";
			break;
		default :
			command = name + dir + " " + line + " " + s + "\n";
			break;
        }
	
	broadcast_message(command);
}


void sniff_start()
{
	if (ipc_id >= 0) 
		{
			create_notice("Already connected to SNiFF+. Select\n\
the STOP icon if you wish to disconnect from\n\
SNiFF+.\n\n", " Ok ");
			return;
		}
	start_server_process();
}


void sniff_stop()
{    
	int     ret;
	
	if (ipc_id < 0)
		return;
	
	ret = create_notice("Are you sure you wish to disconnect from\n\
SNiFF+ ?\n\n",
						" Disconnect ", " Cancel ");
	
	if (ret != 0)
		return;
	
	set_busy_state(TRUE);
	kill_server_process();
	set_busy_state(FALSE);
}

static void sc_process_death(int ipc_id)
{
	create_notice("SNiFF+ Message server has died.", " Ok ");
	ipc_id = -1;
}

static void sc_read_callback(int ipc_id)
{       
	string  str, s;
	string filname;
	list    lines, tokens;
	string test;
	int	i, p,q;
	
	sprintf(test, "In callback function [%d]", ++counter);
	if ((str = ipc_read(ipc_id)) == "") 
		{
			create_notice("Error whilst reading from the SNiFF IPC link.\n\n", " Ok ");
			return;
		}
	sc_log_string("Rx", input_line);
	input_line += str;
	
	while ((i = index(input_line, "\n")) > 0) 
		{
			s = substr(input_line, 1, i - 1);
			input_line = substr(input_line, i + 1);
			
			tokens = split(s, " \n");
	        switch (substr(s, 1, 1)) 
				{
				case "o" :
				case "O" :
	                filname = substr(tokens[0], 2, strlen(s)-2);
	                edit__file(filname);
					
	                switch (substr(s,1,1)) 
						{
						case "o" :
							goto_line(0);
							beginning_of_line();
							next_char(atoi(tokens[1])-1);
							p = inq_byte_pos();
							search_fwd("[(:]",1);
							q = inq_byte_pos();
							prev_char( q - p );
							search_hilite( (q - p) + 1 );
							
							break;
						case "O" :
							goto_line(atoi(tokens[1]));
							break;
						}
	                break;
				case "L" :
	                failure_message(lines);
					break;
				case "t" :
					failure_message(lines);
					break;
				case "a" :
				case "A" :
	                message( substr(s,2,strlen(s)-1) );
	                break;
				}
		}
	refresh();
}


static void sc_write_string(string str)
{
	ipc_write(ipc_id, str);
	sc_log_string("Tx", str);
}


static declare sc_log_string(string title, string str)
{       
	int     win;
	static int buf = -1;
	
	if (sc_debug) 
		{
			save_excursion();
			
			if (inq_buffer(buf) >= 0)
				set_buffer(buf);
			else 
				{
					buf = create_buffer(LOG_FILE, NULL);
					set_buffer_flags(buf, inq_buffer_flags() & ~BF_NO_UNDO);
				}
			end_of_buffer();
			
			insert("%s: %s", title, str);
			if (inq_views(NULL, win)) 
				{
					set_window(win);
					set_bottom_of_window();
				}

			set_buffer_flags(NULL, inq_buffer_flags(NULL) & ~BF_CHANGED);
			restore_excursion();
		}
}


static void failure_message(string tokens)
{
	message("Received %s message.", tokens);
}


/*  Rgister opening of a new File */
void sc_reg_new_file()
{    
	string  dir, file;
	
	if (inq_system())
		return;
	
	inq_names(file);
	
	dir = substr(file, 1, rindex(file, "/") - 1);
	file = substr(file, rindex(file, "/") + 1);
	
}

/* Register that the file has been saved */
static void sc_reg_write_file(int ipc_id)
{      
	string  dir, file;
	
	if (inq_system())
		return;

	inq_names(file);
	
	dir = dirname(file);
	file = basename(file);
	
	if (file != LOG_FILE)
		sniff_icon("u");
}
